﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Security.Cryptography;
using System.Text;
using Newtonsoft.Json;
using ZilLion.Service.Core.ConfigManager.Attribute;
using ZilLion.Service.Core.ConfigManager.Interface;

namespace ZilLion.Service.Core.ConfigManager.Provider
{
    public class JsonConfigProvider : IConfigProvider
    {
        #region 帮助方法

        /// <summary>
        ///     计算文件的hash值 用于比较两个文件是否相同
        /// </summary>
        /// <param name="filePath">文件路径</param>
        /// <returns>文件hash值</returns>
        public  string GetFileHash(string filePath)
        {
            //创建一个哈希算法对象
            using (var hash = new HMACSHA256 ())
            {
                using (var file = File.OpenRead(filePath))
                {
                    //哈希算法根据文本得到哈希码的字节数组
                    var hashByte = hash.ComputeHash(file);
                    //将字节数组装换为字符串
                    return BitConverter.ToString(hashByte);
                }
            }
        }
        public  string GetFileHash(Stream stream)
        {
            //创建一个哈希算法对象
            using (var hash = new HMACSHA256())
            {
                //哈希算法根据文本得到哈希码的字节数组
                var hashByte = hash.ComputeHash(stream);
                //将字节数组装换为字符串
                return BitConverter.ToString(hashByte);
            }
        }

        public T DeepClone<T>(T @this)
        {
            IFormatter formatter = new BinaryFormatter();
            using (var stream = new MemoryStream())
            {
                formatter.Serialize(stream, @this);
                stream.Seek(0, SeekOrigin.Begin);
                return (T)formatter.Deserialize(stream);
            }
        }
        public object DeepCloneTo(object source, object terget, Type type)
        {
            var re = terget;
            var propertyInfos = type.GetProperties();

            foreach (var prop in propertyInfos)

                try
                {
                    if (!prop.CanWrite) continue;
                    prop.SetValue(re,
                        prop.PropertyType.IsClass
                            ? this.DeepClone(prop.GetValue(source, null))
                            : prop.GetValue(source, null), null);
                }
                catch (Exception e)
                {
                    Console.WriteLine(e);
                }
            return re;
        }

        #endregion



        private readonly object _savelock = new object();

        private string _jsonFilehashcode = string.Empty;

        public virtual object LoadConfig()
        {
            if (NeedReload())
            {
                var jsondic = GetJsonDic();
                if (jsondic != null)
                {
                    var configName = GetConfigName();
                    if (jsondic.ContainsKey(configName))
                    {

                        object temp = JsonConvert.DeserializeObject(jsondic[configName].ToString(), ConfigurationType);
                        if (CurrentConfigInstance == null)
                            CurrentConfigInstance = temp;
                        else
                            DeepCloneTo(temp,CurrentConfigInstance, ConfigurationType);
                    }
                }
            }
            return CurrentConfigInstance;
        }

        public virtual void SaveConfig(object config)
        {
            lock (_savelock)
            {
                var jsondic = GetJsonDic();
                var configName = GetConfigName();
                if (jsondic != null)
                {

                    if (jsondic.ContainsKey(configName))
                        jsondic[configName] = config;
                    else
                        jsondic.Add(configName, config);
                }
                else
                {
                    jsondic = new ConcurrentDictionary<string, dynamic>();
                    jsondic.Add(new KeyValuePair<string, dynamic>(configName, config));

                }



                File.WriteAllText(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "zlsetting.json"),
                    JsonConvert.SerializeObject(jsondic), Encoding.Default);
                CurrentConfigInstance = config;
            }
        }

        public Type ConfigurationType { get; set; }

        public object CurrentConfigInstance { get; set; }

        private string GetConfigName()
        {
            var configName = ConfigurationType.Name;
            var zilLionConfigsectionAttribute =
                ConfigurationType.GetCustomAttribute<ZilLionConfigSectionAttribute>();
            if (zilLionConfigsectionAttribute != null)
                configName = zilLionConfigsectionAttribute.SectionName;

            return configName;
        }


        protected bool NeedReload()
        {
            var result = false;
            var filepath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "zlsetting.json");
            if (!File.Exists(filepath))
            {
                var stream = File.Create(filepath);
                var newhash = this.GetFileHash(stream);
                result = newhash != _jsonFilehashcode;
                _jsonFilehashcode = newhash;
            }
            else
            {
                var newhash = this.GetFileHash(filepath);
                result = newhash != _jsonFilehashcode;
                _jsonFilehashcode = newhash;
            }
            return result;
        }


        private IDictionary<string, dynamic> GetJsonDic()
        {
            IDictionary<string, dynamic> dic = new Dictionary<string, dynamic>();
            try
            {
                var filepath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "zlsetting.json");
                if (File.Exists(filepath))
                {
                    var jsonstring = File.ReadAllText(filepath, Encoding.UTF8);
                    dic = JsonConvert.DeserializeObject<Dictionary<string, dynamic>>(jsonstring);
                }
                else
                {
                    File.WriteAllText(filepath, string.Empty);
                }
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
            }
            return dic;
        }
    }
}